﻿Imports MaasOne.Base

Namespace YahooManaged.Finance.Screener.API


    Public Class BondScreenerDownload
        Inherits Base.Download

        Public Event AsyncDownloadCompleted(ByVal sender As Base.Download, ByVal e As BondScreenerDownloadCompletedEventArgs)


        Public Property Options As BondScreenerOptions = New BondScreenerOptions


        Public Function Download() As BondScreenerResponse
            Return Me.ToResponse(MyBase.DownloadStream(Me.DownloadUrl()))
        End Function

        Public Sub DownloadAsync(Optional ByVal userArgs As Object = Nothing)
            Dim args As New AsyncArgs(userArgs, New BondScreenerOptions(Me.Options))
            MyBase.DownloadStreamAsync(Me.DownloadUrl, args)
        End Sub

        Private Sub AsyncDownload_Completed(ByVal sender As Base.Download, ByVal e As Base.StreamDownloadCompletedEventArgs) Handles MyBase.AsyncStreamDownloadCompleted
            Dim args = CType(e.UserArgs, AsyncArgs)
            RaiseEvent AsyncDownloadCompleted(Me, New BondScreenerDownloadCompletedEventArgs(args.UserArgs, Me.ToResponse(e.Response), args.Options))
        End Sub

        Private Function DownloadUrl() As String
            Dim whereClause As New Text.StringBuilder
            Dim convCulture As New Globalization.CultureInfo("en-US")
            With Me.Options
                whereClause.Append("url=""http://reports.finance.yahoo.com/z1?")

                Dim index As Double = .Start - (.Start Mod 15)
                If index > 0 Then index /= 15
                index += 1
                whereClause.AppendFormat("&b={0}", index)

                If .Types.Contains(BondType.Treasury) Then whereClause.Append("&tt=1")
                If .Types.Contains(BondType.TreasuryZeroCoupon) Then whereClause.Append("&tz=1")
                If .Types.Contains(BondType.Corporate) Then whereClause.Append("&tc=1")
                If .Types.Contains(BondType.Municipal) Then
                    whereClause.Append("&tz=1")
                    If .Municipal_State <> UsState.Any Then whereClause.AppendFormat("&stt={0}", .Municipal_State.ToString)
                Else
                    whereClause.Append("&stt=-")
                End If
                Select Case .Price
                    Case PriceType.Any : whereClause.Append("&pr=0")
                    Case PriceType.Premium : whereClause.Append("&pr=1")
                    Case PriceType.Par : whereClause.Append("&pr=2")
                    Case PriceType.Discount : whereClause.Append("&pr=3")
                End Select
                If .CouponRange.Minimum >= 0 Then
                    whereClause.AppendFormat("&cpl={0}", .CouponRange.Minimum.ToString(convCulture))
                Else
                    whereClause.Append("&cpl=-1")
                End If
                If .CouponRange.Maximum >= 0 Then
                    whereClause.AppendFormat("&cpu={0}", .CouponRange.Maximum.ToString(convCulture))
                Else
                    whereClause.Append("&cpu=-1")
                End If

                If .CurrentYieldRange.Minimum >= 0 Then
                    whereClause.AppendFormat("&yl={0}", .CurrentYieldRange.Minimum.ToString(convCulture))
                Else
                    whereClause.Append("&yl=-1")
                End If
                If .CurrentYieldRange.Maximum >= 0 Then
                    whereClause.AppendFormat("&yu={0}", .CurrentYieldRange.Maximum.ToString(convCulture))
                Else
                    whereClause.Append("&yu=-1")
                End If

                If .YTMRange.Minimum >= 0 Then
                    whereClause.AppendFormat("&ytl={0}", .YTMRange.Minimum.ToString(convCulture))
                Else
                    whereClause.Append("&ytl=-1")
                End If
                If .YTMRange.Maximum >= 0 Then
                    whereClause.AppendFormat("&ytu={0}", .YTMRange.Maximum.ToString(convCulture))
                Else
                    whereClause.Append("&ytu=-1")
                End If

                If .MaturityRangeInMonths.Minimum >= 0 Then
                    whereClause.AppendFormat("&mtl={0}", .MaturityRangeInMonths.Minimum.ToString)
                Else
                    whereClause.Append("&mtl=-1")
                End If
                If .MaturityRangeInMonths.Maximum >= 0 Then
                    whereClause.AppendFormat("&mtu={0}", .MaturityRangeInMonths.Maximum.ToString)
                Else
                    whereClause.Append("&mtu=-1")
                End If

                If .RatingRange.Minimum <> Rating.Any Then
                    whereClause.AppendFormat("&rl={0}", CInt(.RatingRange.Minimum).ToString)
                Else
                    whereClause.Append("&rl=-1")
                End If
                If .RatingRange.Maximum <> Rating.Any Then
                    whereClause.AppendFormat("&ru={0}", CInt(.RatingRange.Maximum).ToString)
                Else
                    whereClause.Append("&ru=-1")
                End If

                If .Callable.HasValue Then
                    whereClause.AppendFormat("&cll={0}", CInt(IIf(.Callable.Value = True, 1, 0)).ToString)
                Else
                    whereClause.Append("&cll=-1")
                End If

                Select Case .RankFor
                    Case BondProperty.Type : whereClause.Append("&sf=t")
                    Case BondProperty.Issue : whereClause.Append("&sf=i")
                    Case BondProperty.Price : whereClause.Append("&sf=p")
                    Case BondProperty.CouponInPercent : whereClause.Append("&sf=c")
                    Case BondProperty.Maturity : whereClause.Append("&sf=m")
                    Case BondProperty.YtmInPercent : whereClause.Append("&sf=y")
                    Case BondProperty.CurrentYieldInPercent : whereClause.Append("&sf=Y")
                    Case BondProperty.FitchRatings : whereClause.Append("&sf=r")
                    Case BondProperty.Callable : whereClause.Append("&sf=l")
                End Select
                Select Case .RankDirection
                    Case ListSortDirection.Ascending : whereClause.Append("&so=a")
                    Case ListSortDirection.Descending : whereClause.Append("&so=d")
                End Select

                whereClause.Append(""" AND xpath='//table[@class=""yfnc_tableout1""]/tr/td/table/tr'")
            End With

            Return mHelper.YqlUrl("*", "html", whereClause.ToString, Nothing, False)
        End Function


        Private Function ToResponse(ByVal resp As Base.StreamResponse) As BondScreenerResponse
            Dim results As New List(Of BondScreenerData)
            Dim convCulture As New Globalization.CultureInfo("en-US")
            Dim resultsNodes = mHelper.GetResultsFromXml(resp.Result, "results")
            If resultsNodes.Length = 1 Then
                Dim resultsNode = resultsNodes(0)
                For Each trNode As XmlNode In resultsNode.ChildNodes
                    Dim classAtt = trNode.Attributes("class")
                    If classAtt IsNot Nothing Then
                        If classAtt.Value = "yfnc_tabledata1" Then
                            Dim res As New BondScreenerData
                            Dim index As Integer = 0
                            For Each tdNode As XmlNode In trNode.ChildNodes
                                Select Case index
                                    Case 0
                                        Dim fontNode = tdNode.FirstChild
                                        Select Case fontNode.InnerText
                                            Case "Treas" : res.Type = BondType.Treasury
                                            Case "Zero" : res.Type = BondType.TreasuryZeroCoupon
                                            Case "Corp" : res.Type = BondType.Corporate
                                            Case "Muni" : res.Type = BondType.Municipal
                                        End Select
                                    Case 1
                                        Dim aNode = tdNode.FirstChild.FirstChild
                                        Dim fontNode = tdNode.LastChild.FirstChild
                                        res.Issue = New Link(fontNode.InnerText.Replace(Chr(10), ""), New Uri("http://reports.finance.yahoo.com" & aNode.Attributes("href").Value))
                                    Case 2
                                        Dim fontNode = tdNode.FirstChild.FirstChild
                                        Double.TryParse(fontNode.InnerText, Globalization.NumberStyles.Any, convCulture, res.Price)
                                    Case 3
                                        Dim fontNode = tdNode.FirstChild.FirstChild
                                        Double.TryParse(fontNode.InnerText, Globalization.NumberStyles.Any, convCulture, res.CouponInPercent)
                                    Case 4
                                        Dim nobrNode = tdNode.FirstChild.FirstChild.FirstChild
                                        Date.TryParseExact(nobrNode.InnerText, "dd-MMM-yyyy", convCulture, Globalization.DateTimeStyles.None, res.Maturity)
                                    Case 5
                                        Dim fontNode = tdNode.FirstChild.FirstChild
                                        Double.TryParse(fontNode.InnerText, Globalization.NumberStyles.Any, convCulture, res.YieldToMaturityInPercent)
                                    Case 6
                                        Dim fontNode = tdNode.FirstChild.FirstChild
                                        Double.TryParse(fontNode.InnerText, Globalization.NumberStyles.Any, convCulture, res.CurrentYieldInPercent)
                                    Case 7
                                        Dim fontNode = tdNode.FirstChild
                                        For Each r As Rating In [Enum].GetValues(GetType(Rating))
                                            If r.ToString = fontNode.InnerText Then
                                                res.FitchRating = r
                                                Exit For
                                            End If
                                        Next
                                    Case 8
                                        Dim fontNode = tdNode.FirstChild
                                        res.Callable = CBool(IIf(fontNode.InnerText = "Yes", True, False))
                                End Select
                                index += 1
                            Next
                            results.Add(res)
                        End If
                    End If
                Next
            End If
            Return New BondScreenerResponse(resp.Connection, results.ToArray)
        End Function

        Private Class AsyncArgs
            Inherits Base.DownloadEventArgs

            Public Options As BondScreenerOptions

            Public Sub New(ByVal userArgs As Object, ByVal opt As BondScreenerOptions)
                MyBase.New(userArgs)
                Me.Options = opt
            End Sub

        End Class

    End Class


    Public Class BondScreenerResponse
        Inherits Base.Response

        Public Overloads ReadOnly Property Result As BondScreenerData()
            Get
                Return CType(MyBase.Result, BondScreenerData())
            End Get
        End Property

        Friend Sub New(ByVal conn As Base.ConnectionInfo, ByVal results() As BondScreenerData)
            MyBase.New(conn, results)
        End Sub

    End Class


    Public Class BondScreenerDownloadCompletedEventArgs
        Inherits Base.DownloadCompletedEventArgs

        Private mOptions As BondScreenerOptions

        Public Overloads ReadOnly Property Response As BondScreenerResponse
            Get
                Return CType(MyBase.Response, BondScreenerResponse)
            End Get
        End Property
        Public ReadOnly Property Options As BondScreenerOptions
            Get
                Return mOptions
            End Get
        End Property

        Friend Sub New(ByVal userArgs As Object, ByVal resp As BondScreenerResponse, ByVal opt As BondScreenerOptions)
            MyBase.New(userArgs, resp)
            mOptions = opt
        End Sub

    End Class


    Public Class BondScreenerOptions
        Inherits ResultCountOptions

        Public Property Types As List(Of BondType) = New List(Of BondType)
        Public Property Municipal_State As UsState = UsState.Any
        Public Property Price As PriceType = PriceType.Any
        Public Property CouponRange As Range(Of Double) = New Range(Of Double) With {.Maximum = -1, .Minimum = -1}
        Public Property CurrentYieldRange As Range(Of Double) = New Range(Of Double) With {.Maximum = -1, .Minimum = -1}
        Public Property YTMRange As Range(Of Double) = New Range(Of Double) With {.Maximum = -1, .Minimum = -1}
        Public Property MaturityRangeInMonths As Range(Of Integer) = New Range(Of Integer) With {.Maximum = -1, .Minimum = -1}
        Public Property RatingRange As Range(Of Rating) = New Range(Of Rating) With {.Maximum = Rating.Any, .Minimum = Rating.Any}
        Public Property Callable As Nullable(Of Boolean) = Nothing

        Public Property RankFor As BondProperty = BondProperty.Maturity
        Public Property RankDirection As ListSortDirection = ListSortDirection.Ascending

        Public Sub New()
            MyBase.Count = 15
        End Sub
        Friend Sub New(ByVal original As BondScreenerOptions)
            With original
                Me.Start = .Start
                Me.Count = .Count
                For Each t As BondType In .Types
                    Me.Types.Add(t)
                Next
                Me.Municipal_State = .Municipal_State
                Me.Price = .Price
                Me.CouponRange = New Range(Of Double) With {.Maximum = original.CouponRange.Maximum, .Minimum = original.CouponRange.Minimum}
                Me.CurrentYieldRange = New Range(Of Double) With {.Maximum = original.CurrentYieldRange.Maximum, .Minimum = original.CurrentYieldRange.Minimum}
                Me.YTMRange = New Range(Of Double) With {.Maximum = original.YTMRange.Maximum, .Minimum = original.YTMRange.Minimum}
                Me.MaturityRangeInMonths = New Range(Of Integer) With {.Maximum = original.MaturityRangeInMonths.Maximum, .Minimum = original.MaturityRangeInMonths.Minimum}
                Me.RatingRange = New Range(Of Rating) With {.Maximum = original.RatingRange.Maximum, .Minimum = original.RatingRange.Minimum}
                Me.Callable = .Callable
                Me.RankFor = .RankFor
                Me.RankDirection = .RankDirection
            End With
        End Sub

    End Class

End Namespace
